<?php
/**
 * config.class
 *
 * script
 *
 * @version:	0.1a
 * @package:	global
 * @author:	alex <qfox@ya.ru> yaroshevich
 * @created:	21 jan 2009
 *
 * @copyright:	(c) 2009 flamebox. All rights reserved.
 */

class config extends singleton {

	// php singleton wrapper
	static public function &instance( ) { return parent::instance( __CLASS__ ); }

	protected $q = null; // parsed ini file
	protected $file = null; // file name
	protected $files = array( );
	protected $basepath = null;
	protected $paths = array( );

	protected function relpath( $file ) {
		return str_replace($this->basepath,'./',realpath($file));
	}

	protected function conffiles() {
		$r = array();

		foreach(glob($this->basepath.'*.*') as $f) {
			if ($f[0] === '.' || !is_readable($f) || !is_file($f) || pathinfo($f,PATHINFO_EXTENSION) == 'dist')
				continue;
			$r[$this->relpath($f)] = $f;
		}

		return $r;
	}

	protected function parse( $file, $loop = 0, $base = '/' ) {

		//$gds = array( '', ROOT.'.confs/', __CORE, null );
		//foreach( $gds as $gd ) if( is_readable( $gd.$file ) ) break;
		//if( !is_readable( $gd.$file ) ) return null;

		$relpath = $this->relpath($file);
		if (isset($this->files[$relpath])) die( sprintf( '%s already included to %s', $relpath, $this->files[$relpath]) );

		// including @
		$x = parse_ini_file($file, 1);
		$this->files[$relpath] = $base;

		if( $loop < 2 ) {

			if( isset( $x['@'] ) ) {
				if ( false !== ($subfile = $this->fetch_config_file( $x['@'] )) )
					$x += (array)($this->parse( $subfile, $loop+1 ));
				unset( $x['@'] );
			}

			foreach( array_keys( $x ) as $k ) {
				if( !is_array( $x[$k] ) || !isset( $x[$k]['@'] ) )
					continue;
				if( false !== ($subfile = $this->fetch_config_file( $x[$k]['@'] )) )
					$x[$k] += (array)($this->parse( $subfile, $loop+1, $base . $k . '/' ));
				unset( $x[$k]['@'] );
			}
		}

		if (!empty($x['path']))
			$this->paths[trim($base,'/')] = $x['path'];

		return $x;
	}

	protected function fillin( $x = null ) {

		if(is_null($x)) $x = &$this->q;

		foreach( array_keys( $x ) as $k ) {

			if( is_array( $x[$k] ) ) {
				$x[$k] = $this->fillin($x[$k]);
				continue;

			} elseif( false === strpos($x[$k],'$') )
				continue;

			if(!preg_match_all('@\$([a-z][a-z0-9\_]+)@',$x[$k],$m) || empty($m[1]))
				continue;

			$replaces = array(
				'$root/'	=> ROOTPATH,
				'$core/'	=> BASEPATH,
				'$app/'		=> APPPATH,
				'$exts/'	=> APPPATH,
				'$site/'	=> SITEPATH,
				);

			foreach($m[1] as $paramkey)
				if($v = $this->$paramkey)
					$replaces['$'.$paramkey] = $v;

			uksort($replaces,create_function('$a,$b','return (strlen($a)==strlen($b)) ? 0 : (strlen($a)>strlen($b) ? -1 : 1);'));

			$x[$k] = str_replace( array_keys($replaces), array_values($replaces), $x[$k] );
		}

		return $x;
	}

	protected function define_paths( $paths ) {
		
		$this->paths = array();
		if( empty($paths) || !is_array($paths) )
			return;

		foreach($paths as $k => $v) {
			if (!file_exists($v)) {
				if ($v != '/' && strpos($v,ROOTPATH) === 0) {
					if (!@mkdir($v, 0750, true))
						die(sprintf('Path %s must be writable',$v));
				} else
					continue;
			}

			$path = realpath( $v );
			if ($path == '/' || !is_dir($path))
				continue;

			$path = $path.'/';
			$this->paths[$k] = $path;
			define( strtoupper( $k.'PATH' ), $path );
		}
	}
	
	protected function fetch_config_file( $file ) {
		
		$file =  pathinfo(ltrim( $file, '.' ), PATHINFO_FILENAME);
		$dotfile = '.' . $file;
		$suggestions = array(
			$file . '.ini',
			$file,
			$dotfile,
			);

		$r = fetch_file( $this->basepath, $suggestions );
		if( $r !== false )
			return $r;
		
		$r = fetch_file( array( APPPATH, BASEPATH ), $dotfile );
		return $r;
	}
	
	protected function __construct( ) {
		
		$this->basepath = ROOT.'.confs/';
		$this->file = $file = $this->fetch_config_file( 'settings' );

		if( $file === false )
			return;
		
		$x = $this->parse($this->file);

		foreach($this->conffiles() as $fk => $f) {
			if (isset($this->files[$fk])) continue;
			$sub = pathinfo($fk,PATHINFO_FILENAME);
			if (!isset($x[$sub]) || !is_array($x[$sub]))
				$x[$sub] = array();
			$x[$sub] = $this->parse($f,1,'/'.$sub.'/') + $x[$sub];
		}

		//var_dump( $this->files, $x['assets'] ); die;

		$x['host'] = pick( @$x['host']['host'], @$x['host'], @$_SERVER['SERVER_NAME'], @$_SERVER['HOST_NAME'], 'console' );
		$x['db_url'] = pick( @$x['db']['url'], @$x['db_url'], null );

		define( 'HOST', $x['host'], 1 );
		define( 'DB_URL', $x['db_url'], 1 );
		
		$x['host_url'] = str_replace( '$host', HOST, _::pick( @$x['host_url'], '/' ) ); // for subdir site
		if( !isset( $x['urls'] ) )
			$x['urls'] = array( );

		if( is_array( $x['urls'] ) )
			foreach( $x['urls'] as $k => $v )
				$x['urls'][$k] = str_replace( '$host', HOST, $v );

		// i18n
		if( !isset( $x['locale'] ) ) $x['locale'] = null;
		$this ->locale = $x['locale'];
		if( is_callable( 'gettext' ) && defined( 'LOCALEPATH' ) )
		{
			$directory = LOCALEPATH;
			$domains = array_filter( array( @$x['locale']['domain'], 'messages' ) );
			foreach( $domains as $domain )
				bindtextdomain( $domain, $directory );
			
			// Choose default domain
			textdomain( reset( $domains ) );
		}
		// i18n configured

		$x['paths'] = $this->paths
			+ (isset($x['paths']) && $x['paths'] ? $x['paths'] : array())
			+ (isset($x['dirs']) && is_array($x['dirs']) ? $x['dirs'] : array())
			+ (isset($x['dir']) && is_array($x['dir']) ? $x['dir'] : array());
		unset($x['dirs']);
		unset($x['dir']);

		$this->q = $x;
		$this->q = $x = $this->fillin();

		$this->define_paths($x['paths']);
	}
	
	public function __set( $p, $v )
	{
		// ->locale__lang => [lang,locale,]
		// ->locale => [locale,]
		//if (bc::enabled()) bc::log( $v, $p );

		list( $p, $ns ) = array_reverse( explode( '__', '__'.$p ) );
		if( !( empty($ns) && $p == 'locale' || $ns == 'locale' && !empty( $p ) ) ) return;
		
		if( empty( $ns ) && !is_array( $v ) ) $v = array( 'all' => $v, 'numeric' => 'C' ); // defaults
		if( empty( $ns ) ) { foreach( $v as $k => $vv ) $this ->__set( 'locale__'.$k, $vv ); return; }
		
		// $this ->q[$ns][$p] = $v; // a ono nado?
		
		if( $p == 'lang' ) { putenv( 'LANG='.$v ); $p = 'all'; }
		
		$p = strtoupper( 'LC_'.$p );
		if( defined( $p ) ) setlocale( constant( $p ), $v );
	}
	
	public function __get( $k )
	{
		list( $k, $ns ) = array_reverse( explode( '__', '__'.$k ) );
		
		//exit( 'qwe'.$k );
		return empty( $ns )
			? ( isset($this ->q[$k]) ? $this ->q[$k] : null )
			: ( isset($this ->q[$ns][$k]) ? $this ->q[$ns][$k] : null );
	}

	public function render( $k, $data )
	{
		$r = $this ->$k;
		if( is_null( $r ) ) return null;
		
		if( !is_null( $data ) )
		{
			if( is_string( $data ) )
			{
				$_data = func_get_args( );
				array_shift( $_data );
				$keys = explode( ',', array_shift( $_data ) );
				if( count( $keys ) )
				{
					$data = array( );
					foreach( $keys as $k )
						$data[ $k ] = array_shift( $_data );
				}
			}
			if( is_array( $data ) )
			{
				$r = str_replace( array_map( create_function( '$a', 'return "{{$a}}";' ), array_keys( $data ) ), array_values( $data ), $r );
			}
		}

		// \n
		$r = stripcslashes( $r );
		
		// $host
		if( strpos( $r, '$host' ) !== false )
			$r = str_replace( '$host', $this ->host, $r );
		
		return $r;
	}
	
	
	public function show( )
	{
		return $this ->q;
	}
	
	public function exists( )
	{
		return !is_null( $this ->q );
	}
	
	public function generate( $data )
	{
		$x = parse_ini_file( $this ->file.'.dist', 1 );
		
		$x['db_url'] = sprintf( 'mysqli://%s:%s@%s:3306/%s/?charset=utf8', $data['login'], $data['passwd'], $data['dbhost'], $data['dbname'] );
		foreach( array_keys( $x ) as $k )
		if( isset( $data[$k] ) )
			$x[$k] = $data[$k];
		
		foreach( array_keys( $x['meta'] ) as $k )
		if( isset( $data[$k] ) )
			$x['meta'][$k] = $data[$k];
		
		$newfile = '';
		foreach( $x as $k => $vs )
		{
			if( !is_array( $vs ) )
			{
				$newfile .= sprintf( "%s\t\t= \"%s\"\n", $k, $vs );
				continue;
			}
			
			$newfile .= sprintf( "\n[%s]\n", $k );
			foreach( $vs as $k => $v )
			{
				$newfile .= sprintf( "%s\t\t= \"%s\"\n", $k, $v );
			}
			//$newfile .= "\n";
		}
		//echo "<pre>".$newfile."</pre>";
		
		file_put_contents( $this ->file, $newfile );
	}
	
	public function files( )
	{
		return str_replace( ROOT, './', join( ', ', array_unique( $this ->files ) ) );
	}
}

